Workbox是Google開發的一個工具,可以簡單透過幾個指令產生Service Worker
檔案
,方便開發者管理緩存機制,也提高開發者的開發效率。
npm install --save-dev workbox-cli
打開package.json
"scripts": {
...
"generate-sw": "workbox generate:sw"
},
新增一組指令碼generate-sw
現在就可以透過npm run generate-sw
產生Service worker
的檔案。
Service Worker
建置的路徑和檔名(注意不要取跟我們設定的service-worker.js
同名不然會被取代)接著,就會自動產生Service worker
的檔案了。
打開產生的Service Worker
檔案
const fileManifest = [
{
"url": "404.html",
"revision": "a4e2271d19eb1f6f93a15e1b7a4e74dd"
},
...
]
會看到根目錄底下,所有的資源都存在fileManifest
的物件中,revision
後面則是hash過的字串。
拉到最下面
const workboxSW = new self.WorkboxSW();
workboxSW.precache(fileManifest);
這邊建立一個Workbox Service Worker
並且透過precache()
將物件的資源全部快取起來,這就跟我們剛開始學快取的時候,載入網頁預先存取的快取效果一樣。
但現在產生的檔案是將所有資源都快取起來,這並不是我們期望的,
因此我們可以來改workbox-cli-config.js
,當再跑產生檔案時,會根據這檔案設定的內容去產生Service Worker
,上面我們選擇的設定都可以在此檔案中找到。
module.exports = {
"globDirectory": "public\\",
"globPatterns": [
"**/*.{html,ico,json,js,css,JPG,PNG,png}"
],
"swDest": "public/workbox-sw-v1.js",
"globIgnores": [
"..\\workbox-cli-config.js"
]
};
globDirectory
是網站根目錄的位置globPatterns
內容放要快取的檔案格式swDest
檔案輸出的位置globIgnores
不放入快取名單的資源
module.exports = {
"globDirectory": "public\\",
"globPatterns": [
"**/*.{html,ico,json,js,css}",
"src/images/*.{jpg,JPG,png,PNG}"
],
"swDest": "public/workbox-sw-v1.js",
"globIgnores": [
"..\\workbox-cli-config.js",
"aboutUs/**"
]
};
稍微修改了一下globPatterns
,原本會尋訪所有網站資料夾底下所有的圖檔,現在限定在src/images/
資料夾底下的圖檔才快取
另外,globIgnores
中加入aboutUs/
資料夾底下的內容先不快取起來。
這時候問題來了,
假如原本Service Worker
裡面有寫很多監聽事件,那麼現在透過設定檔產生新的Service Worker
,好不容易寫好的程式不就被覆蓋了嗎?
在此建立service-worker-base.js
命名隨意,這檔案會放置我們不想被覆蓋的程式。
原本指令是使用workbox generate:sw
,但此方法會產生一個新的檔案,
因此要改成inject:manifest
藉由注入的方式合併檔案。
"scripts": {
...
"generate-sw": "workbox inject:manifest"
}
importScripts('workbox-sw.prod.v2.1.2.js');
const workboxSW = new self.WorkboxSW();
workboxSW.precache([]);
將自動產生的service worker
檔案中,使用到的檔案,和precache
語法貼過來。
module.exports = {
...
"swSrc": "public/service-worker-base.js",
...
};
設定swSrc
告知要注入的檔案是哪一個。
importScripts('workbox-sw.prod.v2.1.2.js');
const workboxSW = new self.WorkboxSW();
workboxSW.precache([
{
"url": "404.html",
"revision": "a4e2271d19eb1f6f93a15e1b7a4e74dd"
}
]);
執行完後,預先快取的內容正確的注入到precahce([])
的陣列中。
接著處理完預先快取,那麼動態快取呢?
//https://firebasestorage.googleapis.com/v0/b/days-pwas-practice.appspot.co
//https://fonts.gstatic.com/s/materialicons/v34/2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2
workboxSW.router.registerRoute(/.*(?:googleapis|gstatic)\.com.*$/, workboxSW.strategies.staleWhileRevalidate({
cacheName: 'fonts-and-firebase-images'
}));
在該專案中,我們引外用來資源的有兩個url
,
藉由workboxSW.router.registerRoute('url規則',要做的事情)
可以去比對網址,當符合googleapis.com
或gstatic.com
兩個網址時,就進入後面的workboxSW.strategies.staleWhileRevalidate()
,如同fetch
到上述兩種網址時,就將資源放入fonts-and-firebase-images
中。
替換一下原本使用的Service worker
,重整網頁後,會發現跟原本fetch
資源後,的動態快取效果一樣。
在fetch
監聽事件中,我們撰寫過,底下將文章寫入資料庫的程式。
var url = 'https://days-pwas-practice.firebaseio.com/article.json';
if(-1 < event.request.url.indexOf(url)){
event.respondWith(
fetch(event.request)
.then(function(response){
var copyRes = response.clone();
clearAllData('article')
.then(function(){
return copyRes.json();
})
.then(function(data){
console.log('copyRes.json()',data);
for(var key in data){
console.log('key',key);
writeData('article',data[key]);
}
});
return response;
})
);
}
我們可以很簡單的移植這段程式,到workbox
的程式碼中,如下
workboxSW.router.registerRoute('https://days-pwas-practice.firebaseio.com/article.json', function(args){
return fetch(args.event.request)
.then(function(response){
var copyRes = response.clone();
clearAllData('article')
.then(function(){
return copyRes.json();
})
.then(function(data){
console.log('copyRes.json()',data);
for(var key in data){
console.log('key',key);
writeData('article',data[key]);
}
});
return response;
})
}
);
前面的參數放入url
,當fetch
到該網址時,則進入此功能(handleFetch
),
裡面的功能根原本一樣,一樣能透過event.request
捕抓到資源,並達到原本要做的事情。
[https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-sw.Router](Workbox Class)
https://developers.google.com/web/tools/workbox/